package com.diy.sigmund.diycommon.spring.interceptor;

import com.diy.sigmund.diycommon.entity.constant.LogConstant;
import com.diy.sigmund.diycommon.util.json.JacksonUtil;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;

/**
 * 可调用处理拦截器
 * <br>拦截并发请求处理，其中并发结果是通过使用AsyncTaskExecutor代表应用程序执行Callable来获得的。
 * <br>CallableProcessingInterceptor在异步线程中调用Callable任务之前和之后调用，以及在容器线程超时/错误时，或在因任何原因（包括超时或网络错误）完成后调用。
 * <br>作为一般规则，拦截器方法引发的异常将通过分派回容器并使用 Exception 实例作为并发结果来恢复异步处理。此类异常随后将通过HandlerExceptionResolver机制进行处理。
 * <br>handleTimeout方法可以选择一个用于恢复处理的值。
 * <br>自从：3.2
 * <br>作者：罗森·斯托扬切夫，罗伯·温奇
 *
 * @author ylm-sigmund
 * @since 2022/7/31 22:35
 */
public class CustomCallableProcessingInterceptor implements CallableProcessingInterceptor {

    private static final Logger log = LoggerFactory.getLogger(CustomCallableProcessingInterceptor.class);

    /**
     * <br>表示此拦截器未确定任何结果的常量，给后续拦截器一个机会。
     * <br>也可以看看：
     * <br>handleTimeout ， handleError
     */
    // Object RESULT_NONE = new Object();

    /**
     * <br>常量指示此拦截器已处理响应而没有结果，并且不会调用其他拦截器。
     * <br>也可以看看：
     * <br>handleTimeout ， handleError
     */
    // Object RESPONSE_HANDLED = new Object();

    /**
     * <br>在提交Callable以进行并发处理的原始线程中开始并发处理之前调用。
     * <br>这对于在调用Callable之前捕获当前线程的状态很有用。一旦状态被捕获，它就可以被转移到preProcess(NativeWebRequest, Callable)中的新Thread 。捕获 Spring Security 的 SecurityContextHolder 的状态并将其迁移到新的 Thread 是一个有用的具体示例。
     * <br>默认实现为空。
     * <br>参数：
     * <br>request – 当前请求
     * <br>task – 当前异步请求的任务
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception {
        // log.info("beforeConcurrentHandling当前线程的上下文信息={}", JacksonUtil.toJson(ContextHolder.getInstance().getCustomUser()));
        log.info(LogConstant.SPRING_LOG, "CallableProcessingInterceptor beforeConcurrentHandling 开始");
        log.info("在提交Callable以进行并发处理的原始线程中开始并发处理之前调用。");
        CallableProcessingInterceptor.super.beforeConcurrentHandling(request, task);
    }

    /**
     * <br>在执行Callable的异步线程中开始并发处理之后和实际调用Callable之前调用。
     * <br>默认实现为空。
     * <br>参数：
     * <br>request – 当前请求
     * <br>task – 当前异步请求的任务
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public <T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception {
        // log.info("preProcess当前线程的上下文信息={}", JacksonUtil.toJson(ContextHolder.getInstance().getCustomUser()));
        log.info("CallableProcessingInterceptor preProcess");
        log.info("在执行Callable的异步线程中开始并发处理之后和实际调用Callable之前调用。");
        CallableProcessingInterceptor.super.preProcess(request, task);
    }

    /**
     * <br>在Callable在执行Callable的异步线程中产生结果后调用。此方法可能在afterTimeout或afterCompletion之后调用，具体取决于Callable何时完成处理。
     * <br>默认实现为空。
     * <br>参数：
     * <br>request – 当前请求
     * <br>task – 当前异步请求的任务
     * <br>concurrentResult – 并发处理的结果，如果Callable引发异常，则可能是Throwable
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) throws Exception {
        log.info("CallableProcessingInterceptor postProcess concurrentResult:{}", JacksonUtil.toJson(concurrentResult));
        log.info("在Callable在执行Callable的异步线程中产生结果后调用。此方法可能在afterTimeout或afterCompletion之后调用，具体取决于Callable何时完成处理。");
        CallableProcessingInterceptor.super.postProcess(request, task, concurrentResult);
    }

    /**
     * <br>当异步请求在Callable任务完成之前超时时从容器线程调用。实现可能会返回一个值，包括一个Exception ，以代替Callable没有及时返回的值。
     * <br>默认实现总是返回RESULT_NONE 。
     * <br>参数：
     * <br>request – 当前请求
     * <br>task – 当前异步请求的任务
     * <br>回报：
     * <br>并发结果值；如果该值不是RESULT_NONE或RESPONSE_HANDLED ，则恢复并发处理并且不调用后续拦截器
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
        log.error("CallableProcessingInterceptor handleTimeout 执行超时了");
        return CallableProcessingInterceptor.super.handleTimeout(request, task);
    }

    /**
     * <br>在Callable任务完成之前处理异步请求时发生错误时从容器线程调用。实现可能会返回一个值，包括一个Exception ，以代替Callable没有及时返回的值。
     * <br>默认实现总是返回RESULT_NONE 。
     * <br>参数：
     * <br>request – 当前请求
     * <br>task – 当前异步请求的任务
     * <br>t – 请求处理时发生的错误
     * <br>回报：
     * <br>并发结果值；如果该值不是RESULT_NONE或RESPONSE_HANDLED ，则恢复并发处理并且不调用后续拦截器
     * <br>抛出：
     * <br>Exception ——如果出现错误
     * <br>自从：
     * <br>5.0
     */
    @Override
    public <T> Object handleError(NativeWebRequest request, Callable<T> task, Throwable t) throws Exception {
        log.error("CallableProcessingInterceptor handleError", t);
        return CallableProcessingInterceptor.super.handleError(request, task, t);
    }

    /**
     * <br>当异步处理因任何原因（包括超时或网络错误）完成时从容器线程调用。
     * <br>默认实现为空。
     * <br>参数：
     * <br>request – 当前请求
     * <br>task – 当前异步请求的任务
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception {
        // if (Objects.nonNull(ContextHolder.getInstance().getCustomUser())) {
        //     ContextHolder.getInstance().remove();
        //     log.info("CallableProcessingInterceptor 异步处理 清除用户上下文 成功");
        // }
        log.info("当异步处理因任何原因（包括超时或网络错误）完成时从容器线程调用。");
        log.info(LogConstant.SPRING_LOG, "CallableProcessingInterceptor afterCompletion 结束");
        CallableProcessingInterceptor.super.afterCompletion(request, task);
    }
}
